﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.Net;
using System.Text.RegularExpressions;
using TPWrapper;
using TweetPhoto;
using System.Drawing.Imaging;

namespace TPRipper
{
	public partial class frmMain : Form
	{
		public frmMain()
		{
			InitializeComponent(); 
		}

		#region Variables

		public const string CONST_URLFORMAT = "http://twitpic.com/photos/{0}?page={1}";
		public const string CONST_FILEPATH_THUMB = @"/thumbs/";
		public const string CONST_FILEPATH_IMAGES = @"{0}{1}";
        public const string CONST_IMAGE_CAPTION_REGEX = "<title>.*? Twitpic </title>";
        public const string CONST_IMAGE_NORMALSIZE_REGEX = "<img class=\"photo\" id=\"photo-display\" src=\".*?\"></center> ";
        public const string CONST_IMAGE_FULLSIZE_REGEX = "<img src=\".*?\">";
        public const string CONST_IMAGE_FULLURL_REGEX = "<a href=\".*?\">";
        public const string CONST_TWEETPHOTO_API_KEY = "dad12954-a13d-491f-9c28-a9ddb2de3915";

        private string importPath_TEMP;

		public MatchCollection _oldCollection;
        public Dictionary<String, String> _ImageList;
		public Thread _Start;
        public Thread _StartImport;

		private string _PathToSave;
		public string PathToSave
		{
			get { return _PathToSave; }
			set { _PathToSave = value; }
		}

		private string _Username;
		public string Username
		{
			get { return _Username; }
			set { _Username = value; }
		}

		private bool _IsEndOfList;
		public bool IsEndOfList
		{
			get { return _IsEndOfList; }
			set { _IsEndOfList = value; }
		}

        public enum Services
        {
            TweetPhoto,
            yFrog,
            Twitpic
        }

		#endregion

        private void UploadToClient(Services serv, string filePath, string comment)
        {
            switch (serv)
            {
                case Services.TweetPhoto:
                    UploadToTweetPhoto(filePath, comment);
                    break;
            }
        }

        private void UploadToTweetPhoto(String filepath, string comment)
        {
            WriteLineToConsole("Uploading: " + filepath);

            try
            {
                TPResponse<TweetPhotoResponse> resp =
                    TPWrapper.TPWrapper.Upload(
                    txtUsernameImport.Text,
                    txtPassword.Text,
                    CONST_TWEETPHOTO_API_KEY, 
                    filepath, 
                    chkPostToTwitter.Checked, 
                    null, 
                    null, 
                    comment, 
                    String.Empty);

                if (resp.Status == HttpStatusCode.OK)
                {
                    WriteLineToConsole("Successful Upload!");
                    RemoveLineFromTRIP(filepath, comment);
                }
                else
                {
                    WriteLineToConsole("Upload Error: " + resp.Status.ToString());
                }
            }
            catch
            {
                WriteLineToConsole(" Error uploading: " + filepath);
            }
        }

        private void RemoveLineFromTRIP(string code, string message)
        {
            string fileCode = code.Replace(".jpg", String.Empty);
            fileCode = fileCode.Replace(importPath_TEMP, String.Empty);
            string messagePath = importPath_TEMP.Replace(@"\images\", String.Empty);


            RemoveLineFromTextFile(messagePath + @"\message.trip", 
                String.Format("{0}|{1}|", fileCode, message, Environment.NewLine));
        }

        private byte[] GetImageContents(string filePath)
        {
            FileInfo fi = new FileInfo(filePath);
            FileStream fs = fi.OpenRead();
            //Read all bytes into an array from the specified file.
            int nBytes = (int)fs.Length;
            byte[] ByteArray = new byte[nBytes];
            int nBytesRead = fs.Read(ByteArray, 0, nBytes);
            fs.Close();

            return ByteArray;
        }

        private void RemoveLineFromTextFile(string textfile, string linecontents)
        {
            try
            {
                StringBuilder sb = new StringBuilder();
                using (StreamReader sr = new StreamReader(textfile))
                {
                    sb.Append(sr.ReadToEnd());
                }

                sb.Replace(linecontents, String.Empty);

                using (StreamWriter outfile = new StreamWriter(textfile))
                {
                    outfile.Write(sb.ToString());
                }
            }
            catch
            {
                WriteLineToConsole("Error Editing Message Log.");
            }
        }

		private void GetList()
		{
			for (int i = 0; i < 100000; i++)
			{
				String fileURL = String.Format(CONST_URLFORMAT, txtUserName.Text, i + 1);
				WriteLineToConsole(String.Format("Reading Contents Of: {0}", fileURL));
				if (ReadHTMLFromURL(fileURL))
				{
					return;
				}
			}
		}

		private void WriteLineToMessagesFile(string imgCode, string Message)
		{
			string savePath = txtPath.Text + txtUserName.Text;
			string logPath = savePath + "/message.trip";
			try
			{
				StringBuilder sb = new StringBuilder();
				using (StreamReader sr = new StreamReader(logPath))
				{
					sb.Append(sr.ReadToEnd());
                    if (String.IsNullOrEmpty(sb.ToString()))
                    {
                        sb.AppendFormat("{0}|{1}|", imgCode, Message.Trim());
                    }
                    else
                    {
                        sb.AppendFormat("{0}{1}|{2}|", Environment.NewLine, imgCode, Message.Trim());
                    }
				}

				using (StreamWriter outfile = new StreamWriter(logPath))
				{
					outfile.Write(sb.ToString());
				}
			}
			catch
			{
				WriteLineToConsole("Error Writing to Message Log.");
			}
		}

        private Dictionary<String, String> ReadImportFileContents(string fileLocation, string imgDirectory)
        {
            try
            {
                Dictionary<String, String> list = new Dictionary<String, String>();
                using (StreamReader sr = new StreamReader(fileLocation))
                {
                    while (!sr.EndOfStream)
                    {
                        string lineInfo = sr.ReadLine();
                        if (!String.IsNullOrEmpty(lineInfo))
                        {
                            string code = lineInfo.Split(Char.Parse("|"))[0];
                            string message = lineInfo.Split(Char.Parse("|"))[1].Trim();
                            list.Add(String.Format("{0}{1}.jpg", imgDirectory, code), message);
                        }
                    }
                }
                return list;
            }
            catch
            {
                WriteLineToConsole("Error Reading Import File.");
                return new Dictionary<String, String>();
            }
        }

		private void WriteLineToConsole(string text)
		{
			if (txtConsole.InvokeRequired)
			{
				txtConsole.Invoke(new MethodInvoker(delegate { txtConsole.AppendText(String.Format("{0}{1}", text, Environment.NewLine)); }));
                txtConsole.Invoke(new MethodInvoker(delegate { txtConsole.ScrollToCaret(); }));
            }
			else
			{
                // Force closing the thread is the only time this throws an exception
                try
                {
                    txtConsole.AppendText(String.Format("{0}{1}", text, Environment.NewLine));
                    txtConsole.ScrollToCaret();
                }
                catch (ObjectDisposedException ex) {  }
			}
		}

        private void UpdateProgress(int value, int maxValue)
        {
            if (pbImport.InvokeRequired)
            {
                pbImport.Invoke(new MethodInvoker(delegate { pbImport.Maximum = maxValue; }));
                pbImport.Invoke(new MethodInvoker(delegate { pbImport.Value = maxValue; }));
            }
            else
            {
                try
                {
                    pbImport.Maximum = maxValue;
                    pbImport.Value = maxValue;
                }
                catch (ObjectDisposedException ex) { }
            }
        }

		private void Start()
		{
			string savePath = txtPath.Text + txtUserName.Text;
			string imagePath = savePath + "/images/";
            string logPath = savePath + "/message.trip";
			PathToSave = savePath;
			Username = txtUserName.Text;

			if (!Directory.Exists(savePath))
			{
				Directory.CreateDirectory(savePath);
			}

			if (!Directory.Exists(imagePath))
			{
				Directory.CreateDirectory(imagePath);
			}

			if (!File.Exists(logPath))
			{
				File.Create(logPath);
			}

			if (txtConsole.InvokeRequired)
			{
				txtConsole.Invoke(new MethodInvoker(delegate { txtConsole.Text = String.Empty; }));
			}

			GetList();
		}

        private void ReadListAndStartImportProcess()
        {
            int i = 1;
            foreach (KeyValuePair<String, String> pair in _ImageList.Reverse())
            {
                UploadToClient(Services.TweetPhoto, pair.Key, pair.Value);
                i = i + 1;
                UpdateProgress(i, _ImageList.Count);
            }
            WriteLineToConsole("Finished Uploading.");
        }

        private void StartImport()
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "TwitPic Ripper files (*.trip)|*.trip";
            string dir = String.Format(@"C:\twitpic\{0}\" , txtUsernameImport.Text);
            if (!Directory.Exists(dir))
            {
                dir = @"C:\twitpic\";
            }
            dialog.InitialDirectory = dir;
            dialog.Title = "Select a TwitPic Ripper file";
            dialog.Multiselect = false;

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                WriteLineToConsole("Import File Selected: " + dialog.FileName);
                importPath_TEMP = String.Format(@"{0}images\", dialog.FileName.Replace(dialog.SafeFileName, String.Empty));
                _ImageList = ReadImportFileContents(dialog.FileName, importPath_TEMP);

                _StartImport = new Thread(ReadListAndStartImportProcess);
                _StartImport.Start();
            }
            ToggleEditableForm(true);
        }

		public byte[] GetBytesFromUrl(string url)
		{
			byte[] b;
			HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(url);
			WebResponse myResp = myReq.GetResponse();

			Stream stream = myResp.GetResponseStream();
			using (BinaryReader br = new BinaryReader(stream))
			{
				b = br.ReadBytes(4194304);
				br.Close();
			}
			myResp.Close();
			return b;
		}

		public void WriteBytesToFile(string fileName, byte[] content)
		{
			FileStream fs = new FileStream(fileName, FileMode.Create);
			BinaryWriter w = new BinaryWriter(fs);
			try
			{
				w.Write(content);
			}
			finally
			{
				fs.Close();
				w.Close();
			}
		}

		public bool getFullImageUrlsFromSource(string html)
		{
			List<String> results = new List<string>();
            MatchCollection collection = Regex.Matches(html, CONST_IMAGE_FULLURL_REGEX, RegexOptions.IgnoreCase);

			if (collection.Count == 0)
			{
				WriteLineToConsole("   No Images Found!");
				return true;
			}
			else
			{
				if (_oldCollection != null && _oldCollection[2].Value == collection[2].Value)
				{
					WriteLineToConsole("   End Of Collection!");
                    ToggleEditableForm(true);
					return true;
				}
				foreach (Match m in collection)
				{
					if (m.Value != "<a href=\"/session/new\">"
						&& !String.IsNullOrEmpty(CleanUrl(m.Value))
						&& m.Value.ToLower() != String.Format("<a href=\"/photos/{0}/feed.rss\">", Username).ToLower())
					{
						String largeFileURL = String.Format("http://twitpic.com/{0}/full", CleanUrl(m.Value));
						WriteLineToConsole(String.Format("Found Full Image URL At: {0}", largeFileURL));
						ReadHTMLFromFullURL(largeFileURL, CleanUrl(m.Value));
					}
				}
				_oldCollection = collection;
				return false;
			}
		}

		public void getActualImageUrlsFromImageList(string html, string imgCode)
		{
            MatchCollection collection = Regex.Matches(html, CONST_IMAGE_FULLSIZE_REGEX, RegexOptions.IgnoreCase);
			foreach (Match m in collection)
			{
				if (m.Value != "<img src=\"/images/logo-main.png\">")
				{
                    WriteLineToConsole(String.Format("Found Image At: {0}", CleanUrl(m.Value)));
                    string fileToSave = String.Format(CONST_FILEPATH_IMAGES, PathToSave + @"\images\", imgCode + ".jpg");
                    if (!File.Exists(fileToSave))
                    {
                        string fixedURL = CleanUrl(m.Value);
                        byte[] fileContents = GetBytesFromUrl(fixedURL);
                        WriteBytesToFile(fileToSave, fileContents);

                        GetImageComments(html, imgCode);
                    }
                    else
                    {
                        WriteLineToConsole(String.Format("Image Exists: {0}", imgCode));
                    }
				}
			}
		}

		public void GetImageComments(string html, string imgCode)
		{
            MatchCollection collection = Regex.Matches(html, CONST_IMAGE_CAPTION_REGEX, RegexOptions.IgnoreCase);
			foreach (Match m in collection)
			{
				WriteLineToMessagesFile(imgCode, CleanMEssage(m.Value));
			}
		}

		private string CleanMEssage(string messageHTML)
		{
			messageHTML = messageHTML.Replace("<title> ", String.Empty);
			return messageHTML.Replace("on Twitpic </title>", String.Empty);
		}

		private string CleanUrl(string url)
		{
			url = url.Replace("<a href=\"/", String.Empty);
			url = url.Replace("<img src=\"", String.Empty);
			return url.Replace("\">", String.Empty);
		}

		public bool ReadHTMLFromURL(string url)
		{
			try
			{
				WebClient client = new WebClient();
				string code = client.DownloadString(url);
				WriteLineToConsole("Locating Images...");
				return getFullImageUrlsFromSource(code);
			}
			catch
			{
				WriteLineToConsole("   No Contents Found!");
				return true;
			}
		}

        private void ToggleEditableForm(bool CanEdit)
        {
            btnStartBackup.Enabled = CanEdit;
            btnStartImport.Enabled = CanEdit;
        }

        private void SetupFormDefaults()
        {
            this.Text = String.Format("TwitPic-Ripper v{0} - The Twitpic Migration Tool", Application.ProductVersion);
            lblCopyright.Text = String.Format("Copyright {0} {1}", "WunSick", "2010");
            ddlService.SelectedIndex = 0;
        }

		public void ReadHTMLFromFullURL(string url, string imgCode)
		{
			try
			{
				WebClient client = new WebClient();
				string code = client.DownloadString(url);
				WriteLineToConsole("Locating Full Image...");
				getActualImageUrlsFromImageList(code, imgCode);
			}
			catch
			{
				WriteLineToConsole("-- ERROR DETECTED --");
			}
		}

		private void btnStart_Click(object sender, EventArgs e)
		{
            if (!String.IsNullOrEmpty(txtUserName.Text.Trim()))
            {
                ToggleEditableForm(false);
                _Start = new Thread(Start);
                _Start.Start();
            }
            else
            {
                MessageBox.Show("You must enter a Username.", "Twitpic Ripper");
            }
		}

		private void Form1_FormClosing(object sender, FormClosingEventArgs e)
		{
			//_Start.Abort();
		}

		private void Form1_Load(object sender, EventArgs e)
		{
            SetupFormDefaults();
		}

        private void copyAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (txtConsole.InvokeRequired)
            {
                txtConsole.Invoke(new MethodInvoker(delegate { Clipboard.Clear(); }));
                txtConsole.Invoke(new MethodInvoker(delegate { Clipboard.SetText(txtConsole.Text); }));
            }
            else
            {
                Clipboard.Clear();
                Clipboard.SetText(txtConsole.Text);
            }
        }

        private void copySelectedToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (txtConsole.InvokeRequired)
            {
                txtConsole.Invoke(new MethodInvoker(delegate { Clipboard.Clear(); }));
                txtConsole.Invoke(new MethodInvoker(delegate { Clipboard.SetText(txtConsole.SelectedText); }));
            }
            else
            {
                Clipboard.Clear();
                Clipboard.SetText(txtConsole.Text);
            }
        }

        private void txtUsernameImport_TextChanged(object sender, EventArgs e)
        {
            txtUserName.Text = txtUsernameImport.Text;
        }

        private void txtUserName_TextChanged(object sender, EventArgs e)
        {
            txtUsernameImport.Text = txtUserName.Text;
        }

        private void btnStartImport_Click(object sender, EventArgs e)
        {
            if (!String.IsNullOrEmpty(txtUsernameImport.Text.Trim())
                && !String.IsNullOrEmpty(txtPassword.Text.Trim()))
            {
                ToggleEditableForm(false);
                StartImport();
            }
            else
            {
                MessageBox.Show("You must enter a Username/Password before you can import.", "Twitpic Ripper");
            }
        }


	}
}
